﻿using System.ComponentModel;
using Dyao.Book.Application.Abstractions;
using Dyao.Book.ApplicationSpiders;
using Dyao.Book.Contract;
using Dyao.Book.Entity;
using Dyao.Book.Repository.Abstractions;
using Foundation.Core;
using Foundation.IdCreate;
using Foundation.Spider;
using Foundation.Spider.DataFlow;
using Foundation.Spider.DataFlow.Parser;
using Foundation.Spider.Http;
using Foundation.Spider.Selector;
using Foundation.Uow;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Dyao.Book.Application.Spiders
{
    [DisplayName("QiDianBookCatalogueContent")]
    public class QiDianBookCatalogueContentSpider : Spider, IQiDianBookCatalogueContentSpider
    {
        private readonly ISpiderManager _spiderMgr;
        public QiDianBookCatalogueContentSpider(IOptions<SpiderOptions> options,
        DependenceServices services,
        ILogger<QiDianBookCatalogueSpider> logger,
        ISpiderManager spiderMgr) : base(options, services, logger)
        {
            _spiderMgr = spiderMgr;
        }
        protected override async Task InitializeAsync(CancellationToken stoppingToken = default)
        {
            // 添加自定义解析
            AddDataFlow<QiDianCatalogueContentDataParser>();
            AddDataFlow<ConsoleStorage>();

            // 错误信息
            OnRequestError += (request, response) =>
            {
                var bookProp = (CrawCatalogueContentDto)request.Properties.GetOrDefault(QiDianConstant.CatalogueContentRequestBookProp);
                string message = $"Uri:{request.RequestUri}, ReasonPhrase:{response.ReasonPhrase}";
                _spiderMgr.PushMessageAsync(bookProp.BookCatalogueId, OperationTypeEnum.ChapterContent, message);
            };

            // 超时请求
            OnRequestTimeout += (requests) =>
            {
                foreach (var request in requests)
                {
                    var bookProp = (CrawCatalogueContentDto)request.Properties.GetOrDefault(QiDianConstant.CatalogueContentRequestBookProp);
                    string message = $"Uri:{request.RequestUri} RequestTimeout";
                    _spiderMgr.PushMessageAsync(bookProp.BookCatalogueId, OperationTypeEnum.ChapterContent, message);
                }
            };
            await Task.CompletedTask;
        }


        /// <inheritdoc cref="IQiDianBookCatalogueContentSpider.CrawContentByCatalogueAsync"/>
        public async Task<bool> CrawContentByCatalogueAsync(CrawCatalogueContentDto dto, CancellationToken cancellationToken)
        {
            return await CrawlContentByCataloguesAsync(new List<CrawCatalogueContentDto>() { dto }, cancellationToken);
        }

        /// <inheritdoc cref="IQiDianBookCatalogueContentSpider.CrawlContentByCataloguesAsync"/>
        public async Task<bool> CrawlContentByCataloguesAsync(List<CrawCatalogueContentDto> dtos, CancellationToken cancellationToken)
        {
            if (dtos.IsNullOrEmpty())
                return false;

            var requests = new List<Request>();
            foreach (var dto in dtos)
            {
                var request = BuildRequest(dto);
                request.Properties.Add(QiDianConstant.CatalogueContentRequestBookProp, dto);
                requests.Add(request);
            }

            await ExecuteAsync(cancellationToken, async () =>
            {
                await AddRequestsAsync(requests);
            });

            return true;
        }

        /// <summary>
        /// 构建请求
        /// </summary>
        /// <param name="page">页码</param>
        /// <returns></returns>
        public Request BuildRequest(CrawCatalogueContentDto dto)
        {
            var request = new Request(dto.ContentLink)
            {
                // 请求超时10秒
                Timeout = 10000
            };
            request.Headers.Cookie = QiDianConstant.UserCookie;
            request.Headers.UserAgent = QiDianConstant.UserAgent;

            return request;
        }
    }


    public class QiDianCatalogueContentDataParser : DataParser
    {
        public override Task InitializeAsync()
        {
            return Task.CompletedTask;
        }

        /// <summary>
        /// 解析
        /// </summary>
        /// <param name="context">上下文</param>
        /// <returns></returns>
        protected override async Task ParseAsync(DataFlowContext context)
        {
            var bookProp = (CrawCatalogueContentDto)context.Request.Properties.GetOrDefault(QiDianConstant.CatalogueContentRequestBookProp);
            var logger = context.ServiceProvider.GetService<ILogger<QiDianCatalogueContentDataParser>>();
            var spiderMgr = context.ServiceProvider.GetService<ISpiderManager>();
            var idCreater = context.ServiceProvider.GetService<IIdCreater>();
            try
            {
                var bookCatalogues = new List<BookCatalogueContentEntity>();
                // 网页数据解析
                var masterNode = context.Selectable.Select(Selectors.XPath(".//main"));
                var pNodes = masterNode.SelectList(Selectors.XPath(".//p")); // 卷目录

                int rid = 0;
                // 卷
                foreach (var p in pNodes)
                {
                    var code = await idCreater.CreateAsync(IdBusinessType.BookChapterContentRedisId, null, default);
                    rid++;
                    var content = p.Value?.Trim();
                    bookCatalogues.Add(new BookCatalogueContentEntity()
                    {
                        ID = code.ToString(),
                        BookCatalogueId = bookProp.BookCatalogueId,
                        WordsNumber = content.Length,
                        Seq = rid,
                        Content = content,
                    });
                }
                // 插入
                await BulkAsync(context, bookCatalogues);
            }
            catch (Exception ex)
            {
                logger.LogError(ex, ex.Message);
                await spiderMgr.PushMessageAsync(bookProp.BookCatalogueId, OperationTypeEnum.ChapterContent, $"Parse Error:{ex.GetTraceMessage()}");
            }

            await Task.CompletedTask;
        }

        public async Task BulkAsync(DataFlowContext context, List<BookCatalogueContentEntity> contents)
        {
            CancellationToken cancellationToken = default;
            var bookProp = (CrawCatalogueContentDto)context.Request.Properties.GetOrDefault(QiDianConstant.CatalogueContentRequestBookProp);
            var uowMgr = context.ServiceProvider.GetRequiredService<IUnitOfWorkManager>();
            var logger = context.ServiceProvider.GetRequiredService<ILogger<QiDianCatalogueContentDataParser>>();
            var bookCatalogueContentRepo = context.ServiceProvider.GetRequiredService<IBookCatalogueContentRepository>();
            var bookCatalogueRepo = context.ServiceProvider.GetRequiredService<IBookCatalogueRepository>();
            var commonMgr = context.ServiceProvider.GetRequiredService<ICommonManager>();

            if (!contents.Any()) return;
            logger.LogInformation($"开始添加，数量{contents.Count()}");

            var catalogues = new List<BookCatalogueEntity>()
                    {
                        new BookCatalogueEntity(){
                            ID = bookProp.BookCatalogueId,
                            CrawlComplete = true
                        }
                    };

            var log = new OperationLogDto()
            {
                OperationLog = $"爬取正文内容,来源:{bookProp.SourceKey},正文链接:{bookProp.ContentLink}",
                SourceId = bookProp.BookCatalogueId,
                OperationType = OperationTypeEnum.Chapter
            };

            using (var uow = uowMgr.Begin(isTransactional: true, requiresNew: true))
            {
                try
                {
                    await bookCatalogueRepo.BulkUpdateAsync(catalogues, op => new { op.CrawlComplete }, cancellationToken); // 更新爬虫标志
                    // 先删除再插入
                    var delEntites = new List<BookCatalogueContentEntity>()
                    {
                        new BookCatalogueContentEntity(){
                            BookCatalogueId = bookProp.BookCatalogueId,
                        }
                    };
                    await bookCatalogueContentRepo.BulkDeleteAsync(delEntites, bc => new { bc.BookCatalogueId }, cancellationToken);
                    await bookCatalogueContentRepo.BulkInsertAsync(contents, cancellationToken);
                    await commonMgr.AddOperationLogsAsync(log);
                    await uow.CompleteAsync(cancellationToken);
                    logger.LogInformation($"添加成功，数量{contents.Count()}");
                }
                catch (Exception ex)
                {
                    logger.LogError($"添加失败，{ex.Message}", ex);
                    await uow.RollbackAsync();
                    throw;
                }
            }
        }
    }
}
